package framework.classification.entities.lattice.erca;


import java.util.ArrayList;
import java.util.HashMap;


import com.googlecode.erca.Attribute;
import com.googlecode.erca.Entity;
import com.googlecode.erca.clf.Concept;
import framework.classification.entities.ClAbstractEntity;
import framework.entity.ACLFunction;


public class LatticeFunctionRequiredErca extends LatticeAbstractErca {

	private ACLFunction funcmodel;
	private String NOPARA = "@NOPARA";
	private String OUT = "@OUT";

	public void calculateReduced(ClAbstractEntity cl) {

		conceptToExtends = new HashMap<String, ArrayList<String>>();
		realOperations = new HashMap<String, String>();
		abstractOperations = new HashMap<String, String>();
		String name = lattice.getName();

		
		Concept top = lattice.getTop();
		
		
		// a partir des extents, nous allons trouver le modele de la fonction
		//comportant le plus de parametre
		ArrayList<ACLFunction> listextents = new ArrayList<ACLFunction>();
		for (Entity ent : top.getExtent()){
			ACLFunction f = new ACLFunction(ent.getName(),ACLFunction.MODE2);
			listextents.add(f);
		}
		funcmodel = computeModelFunction(listextents,cl);
		
		for (Concept c : lattice.getConcepts()){
			calculatedConceptReduced(c, cl);
		}
		
		
		/*System.out.println("================================");
		System.out.println("==="+ cl.getName());
		System.out.println("================================");
		for (Concept concept : lattice.getConcepts()){

			String s =conceptToExtends.get(concept.getName());
			System.out.println(concept.getName()+" "+s);
		}
		System.out.println();*/
	}




	
	private void calculatedConceptReduced(Concept concept,ClAbstractEntity cl) {

		changeNameConcept(concept);
		//soit le concept à un extent, dans ce cas il represente une fonction deja definis
		if (!concept.getSimplifiedExtent().isEmpty()){
			for (Entity ent : concept.getSimplifiedExtent()){
				ACLFunction aclfunc = new ACLFunction(ent.getName(),ACLFunction.MODE2);
				realOperations.put(aclfunc.toString(),concept.getName());
				if (!conceptToExtends.containsKey(concept.getName()))
					conceptToExtends.put(concept.getName(),new ArrayList<String>());
				if (!conceptToExtends.get(concept.getName()).contains(aclfunc.toString()))
					conceptToExtends.get(concept.getName()).add(aclfunc.toString());

				if (lattice.getTop() == concept) superEntity = aclfunc.toString();
			}
			return ;
		}

		//la hash map va comporter pour chaque parametres dans le modele
		//une liste de parametres presente dans les attributs du concept
		HashMap<String,ArrayList<String>> hash = new HashMap<String, ArrayList<String>>();
		
		//on reserve une place pour les sorties
		hash.put(OUT, new ArrayList<String>());
		for (String in : funcmodel.getIn()){
			hash.put(in,new ArrayList<String>());
		}
		
		//on parcours maintenant les attributs puis on remplie la hashmap
		for (Attribute att : concept.getIntent()){
			//sous forme IN=attribut out OUT=attribut
			String stratt = att.getDescription();
			String type = stratt.substring(0,stratt.indexOf('='));
			String nameAtt = stratt.substring(stratt.indexOf('=')+1);

			//si l'attribut est de type IN
			if (type.equals("IN")){
				if (nameAtt.charAt(0) != '!'){
					String top = cl.getAbstractClassfication().getStructure().getMaxSuperType(nameAtt);
					if (!hash.get(top).contains(nameAtt)) hash.get(top).add(nameAtt);
				}
			}
			else {
				if (!hash.get(OUT).contains(nameAtt)) hash.get(OUT).add(nameAtt);
			}
		}

		//on peut construire notre nouvelle fonction abstraite
		//on va prendre le type le plus generale pour les entre et
		//le plus specifique pour les sorties
		ACLFunction newfunc = new ACLFunction(cl.getName());
		for (String in : funcmodel.getIn()){
			
			String topin = cl.getAbstractClassfication().getStructure().getMaxSuperType(in);
			String p = minTypeInList(hash.get(topin),cl);
			if (p != "void") newfunc.getIn().add(p);
		}
		newfunc.addOut(maxTypeInList(hash.get(OUT), cl));
		
		newfunc.setAbstract(true);
		if (!conceptToExtends.containsKey(concept.getName()))
			conceptToExtends.put(concept.getName(),new ArrayList<String>());
		if (!conceptToExtends.get(concept.getName()).contains(newfunc.toString()))
			conceptToExtends.get(concept.getName()).add(newfunc.toString());

		abstractOperations.put(newfunc.toString(),concept.getName());
		if (lattice.getTop() == concept) superEntity = newfunc.toString();
		

	}

	private String maxTypeInList(ArrayList<String> list,ClAbstractEntity cl){
		ArrayList<String> temp = new ArrayList<String>();
		temp.addAll(list);
		for (String in :list){
			temp.remove(in);
			ArrayList<String> parents = cl.getAbstractClassfication().getStructure().getHierarchyDown().get(in);
			if (parents.containsAll(temp)){
				return in;
			}
			temp.add(in);
		}
		return "ANY";
	}
	
	private String minTypeInList(ArrayList<String> list,ClAbstractEntity cl){
		ArrayList<String> temp = new ArrayList<String>();
		temp.addAll(list);
		for (String in :list){
			temp.remove(in);
			ArrayList<String> parents = cl.getAbstractClassfication().getStructure().getHierarchyUp().get(in);
			if (parents.containsAll(temp)){
				return in;
			}
			temp.add(in);
		}
		//System.out.println("ANY");
		return "void";
	}
	
	
	
	
	private ACLFunction computeModelFunction(ArrayList<ACLFunction> list,ClAbstractEntity cl){

		ACLFunction aclfunc = null;

		for (ACLFunction temp : list){

			ArrayList<String> listInTop = new ArrayList<String>();
			for (String para : temp.getIn()){
				String topPara = cl.getAbstractClassfication().getStructure().getMaxSuperType(para);
				listInTop.add(topPara);
			}
			ACLFunction tempModel = new ACLFunction(cl.getName());
			tempModel.getIn().addAll(listInTop);

			if (aclfunc == null) aclfunc = tempModel;
			else updateFunctionModel(aclfunc,tempModel);
		}


		return aclfunc;
	}

	public void updateFunctionModel(ACLFunction f1,ACLFunction f2){
		f1.getIn().removeAll(f2.getIn());
		f1.getIn().addAll(0,f2.getIn());
	}



	private void changeNameConcept(Concept concept){

		if (concept.getName().contains("_")){
			concept.setName("C"
					+ concept.getName().substring(
							concept.getName().indexOf("_") + 1));
		}

	}



}

